import * as lodash from "lodash";
import { getLogger } from "../ts/log";
import debug from "debug";

/**
 * Echart 画图组件的设计
 */
export interface IDraw<T = any> {
  /**
   * 初始化
   * @param el 可以挂载的 HTML 节点
   */
  init(el: HTMLElement): void;

  /**
   * 执行初始化后置钩子
   */
  afterInit(): void;

  /**
   * 执行初始化前置钩子
   */
  beforeInit(): void;

  /**
   * 重绘整个页面
   */
  update(): void;

  /**
   * 执行绘图操作
   */
  draw(): void;

  /**
   * 设置在初始化之前的操作
   * 执行采用先进先出原则
   * 如可以在初始化之前读取 map 数据
   * @param func
   * @constructor
   */
  addBeforeInit(func: () => void): void;

  /**
   * 设置在 echart 挂载后的函数
   * @param func
   * @constructor
   */
  addAfterInit(func: () => void): void;

  /**
   * 设置数据工人
   * 方便在不同操作中通过工人进行数据的特殊处理
   * @param worker
   */
  setDataWorker(worker: T): void;

  /**
   * 销毁时调用此函数
   */
  dispose(): void;

  /**
   * 设置销毁时的钩子函数
   * 函数执行采用先进后出原则
   * @param func
   */
  addDisposeFunc(func: () => void): void;

  logger: debug.Debugger;
}

/**
 * 封装了基于 div 画图的基本类型，
 *
 * 目前支持 echart 和 two.js 两种绘制工具的渲染
 *
 * 渲染需要自己实现相关类的 update 接口和 draw 接口。
 * 检测到大小改变时，会调用 update接口
 */
export class DrawBaseClass<T = any> implements IDraw<T> {
  protected name = "";
  protected el!: HTMLElement;
  protected rb!: ResizeObserver;
  protected before: Array<VoidFunction> = [];
  protected after: Array<VoidFunction> = [];
  protected dispos_: Array<VoidFunction> = [];
  protected worker: T | undefined;
  public logger = getLogger("ys-ts.draw");

  init(el: HTMLElement): void {
    this.logger = getLogger(`ys-ts.draw.${this.name}`);
    this.el = el;
    this.rb = new ResizeObserver(() => {
      this.logger(`[${this.name && this.name}] resize`);
      this.update();
    });
    this.rb.observe(this.el);
  }

  afterInit(): void {
    this.after.forEach((value) => {
      value();
    });
  }
  beforeInit(): void {
    this.before.forEach((value) => {
      value();
    });
  }

  update() {
    this.logger(`${this.name} 没有更新函数`);
  }

  draw() {
    this.logger(`${this.name} 没有绘图函数`);
  }

  addBeforeInit(func: () => void): void {
    this.before.push(func);
  }
  addAfterInit(func: () => void): void {
    this.after.push(func);
  }

  setDataWorker(worker: T): void {
    this.worker = worker;
  }

  addDisposeFunc(func: () => void) {
    this.dispos_.push(func);
  }

  dispose(): void {
    lodash.forEachRight(this.dispos_, (value) => {
      value();
    });
  }
}
